﻿using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Administration;
using System.Text.RegularExpressions;

/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

namespace SP2010List404Handler.Layouts.SP2010List404Handler
{
    public partial class URLRedirectHandler : LayoutsPageBase
    {
        private const string webAppPropertyKey = "Custom404Path";
        private const string webAppPropertyForGuid = "URLRedirectListGuid";

        protected override void OnPreInit(EventArgs e)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                // Ensure the page's master page is set to the master page of this site collection's root web master page.

                using (SPSite thisSite = new SPSite(SPContext.Current.Site.Url))
                {
                    SPWebApplication webApp = thisSite.WebApplication;
                    using (SPWeb urlRedirectWeb = webApp.Sites[0].RootWeb)
                    {
                        this.Page.MasterPageFile = urlRedirectWeb.MasterUrl;
                    }
                }
            });
            base.OnPreInit(e);

        }
        protected override void OnPreRender(EventArgs e)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite thisSite = new SPSite(SPContext.Current.Site.Url))
                {
                    SPWebApplication webApp = thisSite.WebApplication;
                    using (SPWeb urlRedirectWeb = webApp.Sites[0].RootWeb)
                    {

                        string custom404Path = "";

                        // Find the original URL from the request's querystring
                        string pageNotFound = Request.QueryString.ToString();
                        pageNotFound = Page.Server.UrlDecode(pageNotFound);
                        pageNotFound = pageNotFound.Replace("404;", "");

                        // Check if the custom 404 path has been specified for this web application
                        if (webApp.Properties.ContainsKey(webAppPropertyKey))
                        {
                            custom404Path = (string)webApp.Properties[webAppPropertyKey];
                        }

                        // Ensure we don't go through an infinite loop of handling 404 requests.
                        // Also, ensure the custom 404 path wasn't specified
                        if ((custom404Path != "") && !(custom404Path.ToLower().Contains("urlredirecthandler.aspx")) && !(pageNotFound.ToLower().Contains(custom404Path.ToLower())))
                        {

                            if (webApp.Properties.ContainsKey(webAppPropertyForGuid))
                            {
                                // Start by finding the URL Redirect list
                                try
                                {
                                    SPList tryUrlRedirectList = urlRedirectWeb.Lists[new Guid(webApp.Properties[webAppPropertyForGuid].ToString())];
                                }
                                catch (Exception ex)
                                {
                                    lblMessage.Text = "It seems the &quot;List Based 404 Handler&quot; Feature has been Activated on this web application, but the URL Redirect list defined when activating it has since been removed (GUID: [" + webApp.Properties[webAppPropertyForGuid].ToString() + "). Your Administrator could try re-activating the web application scoped Featured named <em>List Based 404 Handler</em>. Error: " + ex.Message;
                                    return;
                                }

                                SPList urlRedirectList = urlRedirectWeb.Lists[new Guid(webApp.Properties[webAppPropertyForGuid].ToString())];

                                // Query the URL Redirect list to find all enabled records in order of highest to lowest priority
                                SPQuery urlItemQuery = new SPQuery();
                                urlItemQuery.Query = "<Where>"
                                    + "<Or>"
                                        + "<Eq><FieldRef Name='Disable'/><Value Type='Boolean'>FALSE</Value></Eq>"
                                        + "<IsNull><FieldRef Name='Disable'/></IsNull>"
                                    + "</Or>"
                                    + "</Where>"
                                    + "<OrderBy>"
                                        + "<FieldRef Name='Sorting' Ascending='TRUE' />"
                                    + "</OrderBy>";


                                SPListItemCollection potentialUrls = urlRedirectList.GetItems(urlItemQuery);

                                // Iterate through all records to determine if they match the original URL
                                foreach (SPListItem thisItem in potentialUrls)
                                {

                                    bool useRegularExpressions = false;

                                    try
                                    {

                                        // Check if this record has regular expression handling enabled
                                        if (thisItem.Fields.ContainsField("UseRegularExpressions")) useRegularExpressions = (bool)thisItem["UseRegularExpressions"];

                                        string thisPattern = "";

                                        // If this record doesn't have regular expressions enabled, interpret the matching URL to look for * asterisk 
                                        // characters and turn it into a regular expression
                                        if (!useRegularExpressions)
                                            thisPattern = "^" + thisItem["URL"].ToString().Replace(".", @"\.").Replace("*", ".*") + "$";
                                        
                                        // Otherwise, use the original matching URL as a regular expression
                                        else
                                            thisPattern = thisItem["URL"].ToString();


                                        // Check if the regular expression matches the original URL
                                        if (Regex.IsMatch(pageNotFound, thisPattern))
                                        {

                                            string redirectUrl = "";
                                            string displayMessage = "";
                                            string displayPageTitle = "";
                                            string displayTitleArea = "";

                                            // Grab the four optional fields from this record to determine what to do with the request
                                            if (thisItem.Fields.ContainsField("RedirectURL")) redirectUrl = (string)thisItem["RedirectURL"];
                                            if (thisItem.Fields.ContainsField("DisplayMessage")) displayMessage = thisItem["DisplayMessage"].ToString();
                                            if (thisItem.Fields.ContainsField("DisplayPageTitle")) displayPageTitle = (string)thisItem["DisplayPageTitle"];
                                            if (thisItem.Fields.ContainsField("DisplayTitleArea")) displayTitleArea = (string)thisItem["DisplayTitleArea"];

                                            // Strip all HTML markup from the display message, in case it was left empty of actual text
                                            string cleanedDisplayMessage = Regex.Replace(displayMessage, @"<(.|\n)+?>", "").Trim();

                                            // Check if the redirect URL of this record has been defined
                                            if (redirectUrl != null && redirectUrl != "")
                                            {

                                                // Interpret the results of the regular expression matching so any substitution text is 
                                                // replaced in the redirect URL
                                                if (useRegularExpressions)
                                                    redirectUrl = Regex.Replace(pageNotFound, thisPattern, redirectUrl);
                                                
                                                // Redirect to the URL specified by this record
                                                Response.Redirect(redirectUrl);
                                            }
                                            else
                                            {

                                                // If the redirect URL of this record was not defined, start populating the page contents
                                                if (cleanedDisplayMessage != null) litPageContents.Text = displayMessage;
                                                if (displayPageTitle != null) litPageTitle.Text = displayPageTitle;
                                                if (displayTitleArea != null) litTitleArea.Text = displayTitleArea;
                                            }

                                            // Stop processing any more records
                                            break;
                                        }
                                    }

                                    catch (Exception ex)
                                    {

                                        // If an exception was encountered, update the Message label with an error, and disable this record 
                                        // so it isn't processed in future
                                        lblMessage.Text = "A problem occurred while processing the URL [" + thisItem["URL"] + "]: " + ex.Message;
                                        thisItem["Disable"] = true;
                                        thisItem.Update();
                                    }
                                }
                            }
                            else
                            {
                                // Otherwise, just redirect to the page specified in the web application's properties.
                                Response.Redirect(custom404Path);
                            }
                        }
                    }
                }
            });
        }
    }
}
